package com.elysium.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.elysium.constants.GlobalExceptionCode;
import com.elysium.exception.GlobalException;
import com.elysium.mapper.PermissionMapper;
import com.elysium.pojo.domain.Login;
import com.elysium.mapper.LoginMapper;
import com.elysium.pojo.dto.AdminLoginDto;
import com.elysium.pojo.dto.LoginInfoDto;
import com.elysium.pojo.dto.UpdatePasswordDto;
import com.elysium.pojo.param.RegisterSaveLoginParam;
import com.elysium.pojo.vo.LoginVo;
import com.elysium.properties.SaTokenSettingProperties;
import com.elysium.service.ILoginService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.elysium.template.AppWeChatTemplate;
import com.elysium.utils.AssertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 登录表 服务实现类
 * </p>
 *
 * @author ElysiumNetworks
 * @since 2024-07-31
 */
@Service
public class LoginServiceImpl extends ServiceImpl<LoginMapper, Login> implements ILoginService {

    @Autowired
    private AppWeChatTemplate appWeChatTemplate;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SaTokenSettingProperties saTokenSettingProperties;

    /**
     * 小程序注册保存login
     * @param registerSaveLoginParam
     */
    @Override
    public void registerSaveLogin(RegisterSaveLoginParam registerSaveLoginParam) {
        Login login = new Login();
        BeanUtil.copyProperties(registerSaveLoginParam, login, false);
        login.setCreateTime(new Date());
        login.setVersion(0);
        login.setDeleted(false);
        super.save(login);
    }

    /**
     * 小程序登录
     * @param code
     * @return
     */
    @Override
    public LoginVo wxLogin(String code, Integer type) {
        // 得到openId
        String openId = appWeChatTemplate.getOpenIdByCode(code);
        // 判斷openId是否已经注册
        // 通过 LambdaQueryWrapper 创建一个查询条件，用于查询 Login 表中与指定 openId 匹配的记录
        // eq 方法用于添加等值查询条件，Login::getOpenId 表示查询 `openId` 字段的值是否等于传入的 openId 参数
        // super.getOne 方法会根据构造的查询条件，从数据库中取出第一条符合条件的 Login 数据，如果没有找到则返回 null
        Login login = super.getOne(new LambdaQueryWrapper<Login>()
                .eq(Login::getOpenId, openId)
                .eq(Login::getType, type));
        AssertUtil.isNotNull(login, GlobalExceptionCode.SERVICE_ERROR);
        // 使用sa-token登录
        Long id = login.getId();
        System.out.println(id);
        StpUtil.login(id);
        // 获取token的名字
        String tokenName = StpUtil.getTokenName();
        // 获取token值
        String tokenValue = StpUtil.getTokenValue();
        // 根据当前用户的登录id查询当前用户权限
        List<String> permissions = permissionMapper.selectByLoginId(id);
        // 将去权限存入redis中
        String key = String.format(saTokenSettingProperties.getPermissionKey(), id);
        redisTemplate.opsForValue().set(key, permissions, StpUtil.getTokenTimeout(), TimeUnit.SECONDS);
        LoginInfoDto loginInfoDto = BeanUtil.copyProperties(login, LoginInfoDto.class);
        return new LoginVo(tokenName, tokenValue, permissions, loginInfoDto);
    }

    /**
     * 后台登录实现
     * @param adminLoginDto
     * @return
     */
    @Override
    public LoginVo adminLogin(AdminLoginDto adminLoginDto) {
        // 1.参数校验-JSR303
        // 2.业务校验
        // 2.1.账号是否存在
        Login login = super.getOne(new LambdaQueryWrapper<Login>()
                .eq(Login::getUsername, adminLoginDto.getUsername())
                .eq(Login::getType, adminLoginDto.getType()));
        // 2.2.密码是否正确
        boolean checkpw = BCrypt.checkpw(adminLoginDto.getPassword(), login.getPassword());
        if (!checkpw){
            throw new GlobalException(GlobalExceptionCode.SERVICE_ERROR);
        }
        // 3.业务实现
        // 3.1.使用Sa-Token进行登录
        Long loginId = login.getId();
        StpUtil.login(loginId);
        // 3.2.获取到tokenname、tokenvalue
        String tokenName = StpUtil.getTokenName();
        String tokenValue = StpUtil.getTokenValue();

        // 3.3.获取当前用户权限，存储到redis
        List<String> permissions = permissionMapper.selectByLoginId(loginId);
        String key = String.format(saTokenSettingProperties.getPermissionKey(), loginId);
        redisTemplate.opsForValue().set(key, permissions, StpUtil.getTokenTimeout(), TimeUnit.SECONDS);
        // 3.4.将用户信息封装到logincontext里面
        LoginInfoDto loginInfo = BeanUtil.copyProperties(login, LoginInfoDto.class);
        // 3.5.封装到loginvo返回
        return new LoginVo(tokenName, tokenValue, permissions, loginInfo);
    }

    /**
     * 后台账号登出
     */
    @Override
    public void logout() {
        String key = String.format(saTokenSettingProperties.getPermissionKey(), StpUtil.getLoginIdAsLong());
        redisTemplate.delete(key);
        StpUtil.logout();
    }

    @Override
    public void updatePassword(UpdatePasswordDto updatePassword) {
        // 1.账号是否存在
        Login login = super.getOne(new LambdaQueryWrapper<Login>()
                .eq(Login::getUsername, updatePassword.getUsername())
                .eq(Login::getType, updatePassword.getType()));
        // 2.密码是否正确
        boolean checkpw = BCrypt.checkpw(updatePassword.getOldPassword(), login.getPassword());
        if (!checkpw){
            throw new GlobalException(GlobalExceptionCode.PARAM_ERROR);
        }
        String hashpw = BCrypt.hashpw(updatePassword.getNewPassword(), BCrypt.gensalt());
        login.setPassword(hashpw);
        super.updateById(login);
    }
}
